// Package request 提供HTTP请求功能，支持GET、POST等多种请求方法，包含超时控制、重试机制等特性
package request

import (
	"crypto/tls"
	"net/http"
	"net/url"
	"strings"
	"time"

	"gitee.com/tuboyou/c2/u2"
)

// sharedTransport 共享的HTTP传输配置
var sharedTransport = &http.Transport{
	TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	MaxIdleConns:    100,
	IdleConnTimeout: 90 * time.Second,
}

// 初始化默认HTTP客户端的Transport配置
var _ = func() int {
	http.DefaultClient.Transport = sharedTransport
	return 0
}()

// Request 表示一个HTTP请求
type Request struct {
	Name       string                 `json:"name"`                   // 请求名称
	Method     string                 `json:"method"`                 // 请求方法（GET/POST等）
	Url        *url.URL               `json:"url"`                    // 请求URL
	Query      map[string]string      `json:"query"`                  // URL查询参数
	Body       string                 `json:"body"`                   // 请求体内容
	Headers    map[string]string      `json:"headers"`                // 请求头
	Timeout    int                    `json:"timeout" default:"5000"` // 超时时间（毫秒）
	Retry      int                    `json:"retry" default:"0"`      // 重试次数
	error      error                  // 错误信息
	ExpectBody map[string]interface{} // 期望的响应体结构
}

// NewRequest 创建一个新的Request对象
//
// 参数：
//
//	name - 请求名称
//	method - 请求方法（GET/POST等）
//	uri - 请求URL
//	query - URL查询参数
//	body - 请求体内容
//	headers - 请求头
//	timeout - 超时时间（毫秒）
//	inerror - 初始错误信息
//
// 返回值：
//
//	*Request - 新创建的Request对象
//
// 注意：
//
//  1. 如果未指定method，默认使用GET方法
//  2. 如果未指定timeout，默认使用5000毫秒
//  3. 如果请求体是JSON格式，会自动设置Content-Type为application/json
func NewRequest(name string, method string, uri string, query map[string]string, body string, headers map[string]string, timeout int, inerror error) *Request {
	u, err := url.Parse(uri)
	if headers == nil {
		headers = make(map[string]string)
	}
	if query == nil {
		query = make(map[string]string)
	}
	if timeout == 0 {
		timeout = 5000
	}
	if method == "" {
		method = "GET"
	}
	if headers["Content-Type"] == "" && body != "" {
		if body[0] == '{' || body[0] == '[' {
			headers["Content-Type"] = "application/json"
		} else {
			headers["Content-Type"] = "application/x-www-form-urlencoded"
		}
	}
	method = strings.ToUpper(method)
	var e2 error
	if err != nil {
		e2 = err
	} else {
		e2 = inerror
	}
	return &Request{Name: name, Method: method, Url: u, Query: query, Body: body, Headers: headers, Timeout: timeout, error: e2}
}

// SetExpectBody 设置期望的响应体结构
//
// 参数：
//
//	expectBody - 期望的响应体结构
//
// 返回值：
//
//	*Request - 当前Request对象（支持链式调用）
func (r *Request) SetExpectBody(expectBody map[string]interface{}) *Request {
	r.ExpectBody = expectBody
	return r
}

// GetError 获取请求的错误信息
//
// 返回值：
//
//	error - 当前请求的错误信息
func (r *Request) GetError() error {
	return r.error
}

// Run 执行HTTP请求
//
// 返回值：
//
//	Response - 请求的响应结果
//
// 注意：
//
//  1. 会自动处理重试逻辑
//  2. 会将结果保存到指定路径（如果设置了resultPath）
//  3. 会合并全局header
//
// 示例：
//
//	resp := req.Run()
//	if resp.Error != nil {
//	    log.Println("请求失败:", resp.Error)
//	}
func (r *Request) Run() Response {
	resp := &http.Response{}

	if r.error != nil {
		return newResponseByHttp(-1, resp, u2.TimeNow().UnixMilli(), r, r.error)
	}
	if r.Query != nil {
		val := r.Url.Query()
		for k, v := range r.Query {
			val.Set(k, v)
		}
		r.Url.RawQuery = val.Encode()
	}
	hreq, err := http.NewRequest(r.Method, r.Url.String(), strings.NewReader(r.Body))
	if err != nil {
		return newResponseByHttp(-2, resp, u2.TimeNow().UnixMilli(), r, err)
	}
	if r.Headers != nil {
		for k, v := range r.Headers {
			hreq.Header.Set(k, v)
		}
	}
	client := &http.Client{Timeout: time.Second * time.Duration(r.Timeout) / 1000}
	stTime := u2.TimeNow().UnixMilli()
	resp, err = client.Do(hreq)
	return newResponseByHttp(-3, resp, stTime, r, err)
}

// String 返回Request的字符串表示
//
// 返回值：
//
//	string - JSON格式的请求信息
//
// 注意：
//
//	如果序列化失败，会返回错误信息
func (r *Request) String() string {
	s, err := u2.Struct2JSON(r, true)
	if err != nil {
		return r.Url.RequestURI() + ",请求错误格式" + err.Error()
	}
	return s
}
